package br.com.criativasoft.opendevice.core.util.cmdline;

/**
 * This class holds all the data for an option. This includes the prefix, the key, the separator
 * (for value options), the multiplicity, and all the other settings describing the option. The class
 * is designed to be only a data container from a user perspective, i. e. the user has read-access to
 * any data determined by the {@link Options#check()}, but not access to any of the other methods
 * which are used internally for the operation of the actual check.
 */

public class OptionData {

    private final static String CLASS = "OptionData";

    private Options.Prefix prefix = null;
    private String key = null;
    private boolean detail = false;
    private Options.Separator separator = null;
    private boolean value = false;
    private Options.Multiplicity multiplicity = null;
    private java.util.regex.Pattern pattern = null;
    private int counter = 0;
    private java.util.ArrayList<String> values = null;
    private java.util.ArrayList<String> details = null;

    /**
     * The constructor
     */

    OptionData(Options.Prefix prefix,
               String key,
               boolean detail,
               Options.Separator separator,
               boolean value,
               Options.Multiplicity multiplicity) {

        if (prefix == null) throw new IllegalArgumentException(CLASS + ": prefix may not be null");
        if (key == null) throw new IllegalArgumentException(CLASS + ": key may not be null");
        if (separator == null) throw new IllegalArgumentException(CLASS + ": separator may not be null");
        if (multiplicity == null) throw new IllegalArgumentException(CLASS + ": multiplicity may not be null");

//.... The data describing the option

        this.prefix = prefix;
        this.key = key;
        this.detail = detail;
        this.separator = separator;
        this.value = value;
        this.multiplicity = multiplicity;

//.... Create the pattern to match this option

        if (value) {
            if (separator == Options.Separator.BLANK) {
                if (detail) {
                    pattern = java.util.regex.Pattern.compile(prefix.getName() + key + "((\\w|\\.)+)$");
                } else {
                    pattern = java.util.regex.Pattern.compile(prefix.getName() + key + "$");
                }
            } else {
                if (detail) {
                    pattern = java.util.regex.Pattern.compile(prefix.getName() + key + "((\\w|\\.)+)" + separator.getName() + "(.+)$");
                } else {
                    pattern = java.util.regex.Pattern.compile(prefix.getName() + key + separator.getName() + "(.+)$");
                }
            }
        } else {
            pattern = java.util.regex.Pattern.compile(prefix.getName() + key + "$");
        }

//.... Structures to hold result data

        if (value) {
            values = new java.util.ArrayList<String>();
            if (detail)
                details = new java.util.ArrayList<String>();
        }

    }

    /**
     * Getter method for <code>prefix</code> property
     * <p>
     *
     * @return The value for the <code>prefix</code> property
     */

    Options.Prefix getPrefix() {
        return prefix;
    }

    /**
     * Getter method for <code>key</code> property
     * <p>
     *
     * @return The value for the <code>key</code> property
     */

    String getKey() {
        return key;
    }

    /**
     * Getter method for <code>detail</code> property
     * <p>
     *
     * @return The value for the <code>detail</code> property
     */

    boolean useDetail() {
        return detail;
    }

    /**
     * Getter method for <code>separator</code> property
     * <p>
     *
     * @return The value for the <code>separator</code> property
     */

    Options.Separator getSeparator() {
        return separator;
    }

    /**
     * Getter method for <code>value</code> property
     * <p>
     *
     * @return The value for the <code>value</code> property
     */

    boolean useValue() {
        return value;
    }

    /**
     * Getter method for <code>multiplicity</code> property
     * <p>
     *
     * @return The value for the <code>multiplicity</code> property
     */

    Options.Multiplicity getMultiplicity() {
        return multiplicity;
    }

    /**
     * Getter method for <code>pattern</code> property
     * <p>
     *
     * @return The value for the <code>pattern</code> property
     */

    java.util.regex.Pattern getPattern() {
        return pattern;
    }

    /**
     * Get the number of results found for this option, which is number of times the key matched
     * <p>
     *
     * @return The number of results
     */

    public int getResultCount() {
        if (value) {
            return values.size();
        } else {
            return counter;
        }
    }

    /**
     * Get the value with the given index. The index can range between 0 and {@link #getResultCount()}<code> - 1</code>.
     * However, only for value options, a non-<code>null</code> value will be returned. Non-value options always
     * return <code>null</code>.
     * <p>
     *
     * @param index The index for the desired value
     *              <p>
     * @return The option value with the given index
     * <p>
     * @throws IllegalArgumentException If the value for <code>index</code> is out of bounds
     */

    public String getResultValue(int index) {
        if (!value) return null;
        if (index < 0 || index >= getResultCount())
            throw new IllegalArgumentException(CLASS + ": illegal value for index");
        return values.get(index);
    }

    /**
     * Get the detail with the given index. The index can range between 0 and {@link #getResultCount()}<code> - 1</code>.
     * However, only for value options which take details, a non-<code>null</code> detail will be returned. Non-value options
     * and value options which do not take details always return <code>null</code>.
     * <p>
     *
     * @param index The index for the desired value
     *              <p>
     * @return The option detail with the given index
     * <p>
     * @throws IllegalArgumentException If the value for <code>index</code> is out of bounds
     */

    public String getResultDetail(int index) {
        if (!detail) return null;
        if (index < 0 || index >= getResultCount())
            throw new IllegalArgumentException(CLASS + ": illegal value for index");
        return details.get(index);
    }

    /**
     * Store the data for a match found
     */

    void addResult(String valueData, String detailData) {
        if (value) {
            if (valueData == null) throw new IllegalArgumentException(CLASS + ": valueData may not be null");
            values.add(valueData);
            if (detail) {
                if (detailData == null) throw new IllegalArgumentException(CLASS + ": detailData may not be null");
                details.add(detailData);
            }
        }
        counter++;
    }

    /**
     * This is the overloaded {@link Object#toString()} method, and it is provided mainly for debugging
     * purposes.
     * <p>
     *
     * @return A string representing the instance
     */

    public String toString() {

        StringBuffer sb = new StringBuffer();

        sb.append("Prefix      : ");
        sb.append(prefix);
        sb.append('\n');
        sb.append("Key         : ");
        sb.append(key);
        sb.append('\n');
        sb.append("Detail      : ");
        sb.append(detail);
        sb.append('\n');
        sb.append("Separator   : ");
        sb.append(separator);
        sb.append('\n');
        sb.append("Value       : ");
        sb.append(value);
        sb.append('\n');
        sb.append("Multiplicity: ");
        sb.append(multiplicity);
        sb.append('\n');
        sb.append("Pattern     : ");
        sb.append(pattern);
        sb.append('\n');

        sb.append("Results     : ");
        sb.append(counter);
        sb.append('\n');
        if (value) {
            if (detail) {
                for (int i = 0; i < values.size(); i++) {
                    sb.append(details.get(i));
                    sb.append(" / ");
                    sb.append(values.get(i));
                    sb.append('\n');
                }
            } else {
                for (int i = 0; i < values.size(); i++) {
                    sb.append(values.get(i));
                    sb.append('\n');
                }
            }
        }

        return sb.toString();

    }

}


